package com.heima.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.heima.admin.mapper.AdChannelMapper;
import com.heima.admin.mapper.AdSensitiveMapper;
import com.heima.admin.service.WemediaNewsAutoScanService;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.constants.message.ArticleForEsConstants;
import com.heima.common.exception.CustomException;
import com.heima.feigns.article.ArticleFeign;
import com.heima.feigns.wemedia.WemediaFeign;
import com.heima.model.admin.pojo.AdChannel;
import com.heima.model.article.dto.ArticleDto;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.pojo.WmNews;
import com.heima.model.wemedia.pojo.WmUser;
import com.heima.utils.common.SensitiveWordUtil;
import io.seata.spring.annotation.GlobalTransactional;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.core.KafkaTemplate;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by ZYJ on 2021/5/30 19:02
 */
@Service
@Slf4j
public class WemediaNewsAutoScanServiceImpl implements WemediaNewsAutoScanService {
    @Autowired
    WemediaFeign wemediaFeign;
    @Value("${file.oss.web-site}")
    private String webSite;
    @Autowired
    private AdSensitiveMapper sensitiveMapper;
    @Autowired
    private GreenTextScan greenTextScan;
    @Autowired
    private GreenImageScan greenImageScan;
    @Autowired
    private KafkaTemplate<String,String> kafkaTemplate;

    /**
     * 自媒体文章审核
     *
     * @param wmNewsId
     */
    @GlobalTransactional
    @Override
    public void autoScanByMediaNewsId(Integer wmNewsId) {
        log.info("============自动发布文章方法被触发===============当前处理的文章id=={}", wmNewsId);
        //1. 检查文章id
        if (wmNewsId == null) {
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID);
        }
        //2. 根据文章id远程获取文章信息
        WmNews wmNews = wemediaFeign.findById(wmNewsId);
        //3. 检查文章状态
        if (wmNews == null) {
            log.error("审核的自媒体文章不存在，自媒体的id:{}", wmNewsId);
            throw new CustomException(AppHttpCodeEnum.DATA_NOT_EXIST);
        }
        //4. 如果文章状态为 4 或 8 并且 发布时间小于等于当前时间 调用发布方法
        long now = System.currentTimeMillis();
        if ((wmNews.getStatus().equals(WmNews.Status.ADMIN_SUCCESS.getCode())
                || wmNews.getStatus().equals(WmNews.Status.SUCCESS.getCode()))
                && wmNews.getPublishTime().getTime() <= now) {

            //保存数据
            publishArticle(wmNews);
            log.info("自媒体文章被发布 {}", wmNewsId);
            return;
        }
        //5. 如果文章状态为1 需要审核该文章
        if (wmNews.getStatus().equals(WmNews.Status.SUBMIT.getCode())) {
            //5.1 抽取文章内容中的文本和图片 key:content value:文本内容 key: images value: List<String> url;
            Map<String, Object> contentAndImages = handleTextAndImages(wmNews);
            //5.2 DFA自管理敏感词审核 如果审核失败 状态改为2
            boolean isSensitiveScan = handleSensitive(contentAndImages.get("content").toString(), wmNews);
            if (!isSensitiveScan) return;
            log.info("============通过DFA审核===============当前处理的文章id=={}", wmNewsId);
            //5.3 阿里云文本审核 审核失败状态改为2 不确定状态改为3
            boolean isTextScan = handleTextScan(contentAndImages.get("content").toString(), wmNews);
            if (!isTextScan) return;
            log.info("============通过阿里文本审核===============当前处理的文章id=={}", wmNewsId);
            //5.4 阿里云图片审核 审核失败状态改为2 不确定状态改为3
            boolean isImageScan = handleImagesScan((List) contentAndImages.get("images"), wmNews);
            if (!isImageScan) return;
            log.info("============通过阿里图片审核===============当前处理的文章id=={}", wmNewsId);
            //5.5 如果发布时间大于当前时间, 通过审核 状态改为8
             if(wmNews.getPublishTime().getTime()>now){
              updateWmNews(wmNews,(short) 8,"审核通过");
             }
            //5.6 最后 发布文章 调用对应方法
            publishArticle(wmNews);

        }
    }

    /**
     * 阿里云图片审核
     *
     * @param images
     * @param wmNews
     * @return
     */
    private boolean handleImagesScan(List<String> images, WmNews wmNews) {
        Boolean flag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String) map.get("suggestion");
            if ("review".equals(suggestion)) {
                flag = false;
                updateWmNews(wmNews, (short) 3, "图片中包含不确定因素,转为人工审核");
            }
            if ("block".equals(suggestion)) {
                flag = false;
                updateWmNews(wmNews, (short) 2, "图片中包含色情敏感信息");
            }
        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            updateWmNews(wmNews, (short) 3, "阿里云审核异常失败,转为人工审核");
        }
        return flag;
    }

    /**
     * 阿里云文本审核
     *
     * @param context
     * @param wmNews
     * @return
     */
    private boolean handleTextScan(String context, WmNews wmNews) {
        Boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(context);
            String suggestion = (String) map.get("suggestion");
            if ("review".equals(suggestion)) {
                flag = false;
                updateWmNews(wmNews, (short) 3, "内容中包含不确定因素,转为人工审核");
            }
            if ("block".equals(suggestion)) {
                flag = false;
                updateWmNews(wmNews, (short) 2, "文本中包含敏感词汇");
            }

        } catch (Exception e) {
            e.printStackTrace();
            flag = false;
            updateWmNews(wmNews, (short) 3, "阿里云审核异常失败,转为人工审核");
        }
        return flag;
    }

    /**
     * 自管理敏感词审核
     *
     * @param content
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        Boolean flag = true;
        //先查数据库的敏感词表
        List<String> sensitiveList = sensitiveMapper.findAllSensitive();
        if (CollUtil.isEmpty(sensitiveList)) {
            return flag;
        }
        //调用DFA工具类先组成字典库
        SensitiveWordUtil.initMap(sensitiveList);
        //再拿文本匹配
        Map<String, Integer> matchWords = SensitiveWordUtil.matchWords(content);
        //审核不通过 如果审核失败 状态改为2
        if (matchWords.size() > 0) {
            flag = false;
            updateWmNews(wmNews, (short) 2, "文章包含了敏感词" + matchWords);

        }
        return flag;
    }

    /**
     * 修改文章
     *
     * @param wmNews 自媒体图文内容信息表
     * @param status 要修改的状态
     * @param msg    原因
     */
    private void updateWmNews(WmNews wmNews, short status, String msg) {
        wmNews.setStatus(status);
        wmNews.setReason(msg);
        ResponseResult responseResult = wemediaFeign.updateWmNews(wmNews);
        if (!responseResult.getCode().equals(0)) {
            log.error("远程修改 自媒体文章状态 失败 {}", responseResult.getErrorMessage());
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR, responseResult.getErrorMessage());
        }
    }

    /**
     * 抽取文章中的 所有文本内容 和所有图片内容
     *
     * @param wmNews
     * @return
     */
    private Map<String, Object> handleTextAndImages(WmNews wmNews) {
        Map<String, Object> result = new HashMap<>();
        String content = wmNews.getContent();
        if (StringUtils.isBlank(content)) {
            log.error("文章自动审核失败,自媒体文章内容为空");
            throw new CustomException(AppHttpCodeEnum.PARAM_INVALID, "自媒体文章内容为空");
        }
        List<Map> contentMap = JSONArray.parseArray(content, Map.class);
        String contentStr = contentMap.stream()
                .filter(map -> "text".equals(map.get("type")))
                .map(map -> (String) map.get("value"))
                .collect(Collectors.joining("_hmtt_"));
        contentStr = contentStr + "_hmtt_" + wmNews.getTitle();
        //抽取内容图片和封面图片内容  封面的图片需要拼接前缀
        List<String> images = new ArrayList<>();
        contentMap.stream()
                .filter(map -> "image".equals(map.get("type")))
                .map(map -> (String) map.get("value"))
                .forEach(images::add);
        //封面图有可能为空,所以做一下非空判断
        if (StringUtils.isNotBlank(wmNews.getImages())) {
            Arrays.stream(wmNews.getImages().split(","))
                    .map(url -> webSite + url)
                    .forEach(images::add);
        }
        //去掉封面图片和内容图片的重复 省钱QWQ
        images = images.stream().distinct().collect(Collectors.toList());
        // key:content value:文本内容 key: images value: List<String> url;
        result.put("content", contentStr);
        result.put("images", images);
        return result;
    }

    /**
     * 发布文章的方法(保存APP端文章相关数据)
     *
     * @param wmNews
     */

    private void publishArticle(WmNews wmNews) {
        //保存app相关文章
        ResponseResult responseResult = saveArticle(wmNews);
        if (!responseResult.getCode().equals(0)) {
            log.error("远程调用发布文章接口失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"文章审核保存app文章失败");
        }
        //保存后返回文章id 保存关联关系
        Long articleId = (Long) responseResult.getData();
        wmNews.setArticleId(articleId);
        //修改自媒体文章状态，状态为9  审核通过
        this.updateWmNews(wmNews,(short)9,"文章审核通过");
        // ==============新加代码==============================================
        Map map = new HashMap();
        map.put("articleId",wmNews.getArticleId());
        kafkaTemplate.send(ArticleForEsConstants.ARTICLE_ELASTICSEARCH_ADD, JSON.toJSONString(map));
        log.info("通知search微服务同步到es");
        // ==============新加代码==============================================
    }
    @Autowired
    AdChannelMapper adChannelMapper;
    @Autowired
    ArticleFeign articleFeign;

    /**
     * 发起远程调动保存三张表数据 返回值携带文章id
     * article  article_config article_content
     * @param wmNews
     * @return
     */
    private ResponseResult saveArticle(WmNews wmNews) {
        //封装ArticleDto
        ArticleDto articleDto = BeanUtil.toBeanIgnoreError(wmNews, ArticleDto.class);
        articleDto.setId(null); //id重新置空
        //有可能已经发布过 后来下架编辑后重新上架 没有就保存,有就修改
        if ( wmNews.getArticleId() != null) {
           articleDto.setId(wmNews.getArticleId());
        }
        //封面图片布局
        articleDto.setLayout(wmNews.getType());
        //文章标记  0 普通文章
        articleDto.setFlag((byte) 0);
        //设置作者信息 根据wmNews 关联wmUserId 查出 wmUser信息 (name 和作者的name字段是一致的)
        WmUser wmUser = wemediaFeign.findWmUserById(wmNews.getUserId());
        if(wmUser == null){
            log.error("获取关联自媒体用户失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联自媒体用户失败");
        }
        articleDto.setAuthorName(wmUser.getName());
        //设置频道信息
        AdChannel adChannel = adChannelMapper.selectById(wmNews.getChannelId());
        if(adChannel == null){
            log.error("获取关联频道信息失败");
            throw new CustomException(AppHttpCodeEnum.SERVER_ERROR,"获取关联频道信息失败");
        }
        articleDto.setChannelName(adChannel.getName());

        //发起远程调动保存三张表数据 返回值携带文章id
        return articleFeign.saveArticle(articleDto);
    }
}
